﻿using Microsoft.Owin.Security;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Sidvall.Web.Host
{
    public class DefaultAuthenticationTicketProvider : IAuthenticationTicketProvider
    {
        #region Public Members

        #region GetAuthenticationTicketAsync

        public virtual Task<AuthenticationTicket> GetAuthenticationTicketAsync(AuthenticationTicket authenticationTicket, Security.ServerContent serverContent,
            Sidvall.Security.TokenResponse tokenResponse)
        {
            if (authenticationTicket == null)
                throw new System.ArgumentNullException(nameof(authenticationTicket));

            var subject = (from c in authenticationTicket.Identity.Claims
                           where c.Type == Sidvall.Security.ClaimTypes.Subject
                           select c.Value).FirstOrDefault();
            var claims = GetClaims(subject, serverContent, tokenResponse);
            var identity = GetClaimsIdentity(claims, authenticationTicket.Identity);
            var properties = GetProperties(authenticationTicket.Properties);
            authenticationTicket = new AuthenticationTicket(identity, properties);
            return Task.FromResult(authenticationTicket);
        }

        #endregion
        #region GetClaims

        protected virtual IEnumerable<Claim> GetClaims(string subject, Sidvall.Security.ServerContent serverContent, Sidvall.Security.TokenResponse tokenResponse)
        {
            var claims = new List<Claim>();
            claims.Add(new Claim(Sidvall.Security.ClaimTypes.Subject, subject));
            claims.Add(new Claim(Sidvall.Security.ClaimTypes.Name, serverContent.UserName));
            if (tokenResponse != null)
            {
                claims.Add(new Claim("access_token", tokenResponse.AccessToken));
                claims.Add(new Claim("expires_at", System.DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
                claims.Add(new Claim("refresh_token", tokenResponse.RefreshToken));
                claims.Add(new Claim("id_token", tokenResponse.IdentityToken));
            }
            if (serverContent != null)
            {
                foreach (var userItemClaim in serverContent.UserItem.Claims)
                    claims.Add(new Claim(userItemClaim.ClaimType, userItemClaim.Value));
            }
            return claims;
        }

        #endregion
        #region GetClaimsIdentity

        protected virtual ClaimsIdentity GetClaimsIdentity(IEnumerable<Claim> claims, ClaimsIdentity identity)
        {
            return new ClaimsIdentity(claims, identity.AuthenticationType);
        }

        #endregion
        #region GetProperties

        protected virtual AuthenticationProperties GetProperties(AuthenticationProperties properties)
        {
            return properties;
        }

        #endregion

        #endregion
        #region Private Members

        #region AddClaim

        private void AddClaim(List<Claim> claims, Sidvall.Data.ParameterCollection parameters, string type)
        {
            var value = parameters.ValueToString(type, null);
            if (!string.IsNullOrWhiteSpace(value))
                claims.Add(new Claim(type, value));
        }

        #endregion

        #endregion
    }
}
